home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / quarkout.23 / quarkout.c < prev    next >
C/C++ Source or Header  |  1994-07-16  |  49KB  |  1,998 lines

  1. /*
  2.    quarkoutfile - beseitigt einige Unschoenheiten in Quark Outfiles.
  3.    Copyright (C) 1993,1994  Uwe Ohse
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2 of the License, or
  8.    (at your option) any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.    For questions contact:
  20.    e-mail: (prefered)
  21.    uwe@tirka.gun.de
  22.    Uwe_Ohse@pb2.maus.de
  23.    Uwe Ohse @ ME (inside MausNet)
  24.    snail-mail:
  25.    Uwe Ohse, Drosselstrasse 2, 47055 Duisburg, Germany
  26.  */
  27.  
  28. /*
  29.  * quarkoutfile - ein Hack, um die durch die Quarkdatenbank bedingten
  30.  * fehlenden bzw. fehlerhaften Daten zu ergaenzen.
  31.  *
  32.  * Bitte beachten:
  33.  *   #define Quark Quark Version 1.x
  34.  *
  35.  *   Saemtliche Inkompatibilitaeten liegen in der Datenbank der derzeitigen
  36.  *   Quark begruendet. Diese wird in der kommenden Quark Version II voellig
  37.  *   erneuert, daher wird Quark II diesen Hack nicht mehr benoetigen.
  38.  *
  39.  * Hacker's Guide: Dieses Programm ist ein Hack, entstanden aus einer Laune,
  40.  * geschrieben von jemandem, der 100%ig wusste, was im Quarktausch so
  41.  * ablaeuft. Ich kenne die Zusammenhaenge - schliesslich habe ich weite
  42.  * Teile des Quarktauschs programmiert. Das bedeutet leider auch, dass ich
  43.  * wohl einiges nicht so gut dokumentiert habe, wie es wuenschenswert waere.
  44.  * Wer Teile des Programms verbessert oder es um neue Features ergaenzt
  45.  * hat, schicke mir bitte diffs zu (mit Erlaeuterungen).
  46.  * Ich formatiere die Sourcen mit:
  47.  * indent -gnu -ts4 -i4 -bli0 -bad -bap -ss -bc <source>
  48.  *
  49.  * Prinzipielle Arbeitsweise der Programms:
  50.  *   Der Aufbau einer Message im Outfile ist ungefaehr:
  51.  *   MAUS:                                  Quark
  52.  *   #message-ID                            #message-ID
  53.  *   Vvon                                   Vvon (aber eventuell verkuerzt)
  54.  *   Ggruppe                                Ggruppe (ev. laenger als bei MAUS)
  55.  *   Eeingabezeit                           Euhrzeit des Einfuegens in die Quark
  56.  *   Wbetreff                               Wbetreff (2 Zeichen weniger Platz)
  57.  *   >Von : blahfasel (uhrzeit wie oben)    :Von : blafasel (echte Eingabezeit)
  58.  *   >sonstige Kopfzeilen                   :Sonsige Kopfzeilen
  59.  *   >                                      :
  60.  *   :Messagetext schliesst sich an.        :Messagetext
  61.  * Die Quark kennt auch einige Headerzeilen nicht, die die MAUS kennt (IRON).
  62.  * Deren Informationen kann man allerdings auch aus den Kopfzeilen
  63.  * entnehmen (das sind die mit > beginnenden Zeilen).
  64.  * Und genau das tut quarkoutfile.
  65.  *
  66.  * --------------------------------------------------------------------------
  67.  * 
  68.  * Aenderung von Marcus Endberg:
  69.  *
  70.  * Anpassung an die lange Betreffzeilen der MAUS-Soft 7.94, die mehr als 30
  71.  * Zeichen lange Betreffzeilen erlaubt. Die von mir vorgenommenen Aenderungen
  72.  * habe ich im Source gekennzeichnet.
  73.  *
  74.  * Kontakt: marcus_endberg@pb.maus.de
  75.  *
  76.  */
  77.  
  78. #include <stdio.h>
  79. #include <string.h>
  80. #include <stdlib.h>
  81. #include <time.h>
  82. #include <limits.h>
  83. #include <errno.h>
  84. #include <ctype.h>
  85.  
  86. #include "config.h"
  87.  
  88. #ifdef HAVE_MMAP
  89. #include <sys/types.h>
  90. #include <sys/mman.h>
  91. #include <sys/stat.h>
  92. #include <fcntl.h>
  93. #include <unistd.h>
  94. #endif
  95.  
  96. /* Laenge einer Headerzeile - reicht auch fuer MAUS 9 */
  97. #define HEADLEN    512
  98.  
  99. /* maximale Laenge einer Textzeile - das reicht allemale */
  100. #define LINESIZE 32767
  101.  
  102. /* fuer klinisch tote Systeme ohne verlaessliches argc/argv */
  103. char *myname = "quarkoutfile";
  104.  
  105. #ifdef HAVE_MMAP
  106. caddr_t mmaddr;
  107. size_t mmsize;
  108. int is_mmapped;
  109. #endif
  110.  
  111. /* Liste zur Unterbringung der Kopfzeilen */
  112. typedef struct kentrystruct
  113. {
  114.     unsigned char line[HEADLEN];
  115.     struct kentrystruct *next;
  116. } kentry_t;
  117.  
  118. kentry_t *kanker;                           /* Pointer auf erste Kopfzeile */
  119. kentry_t *klast;                           /* Pointer auf letzte Kopfzeile */
  120.  
  121. typedef struct grpconv_struct
  122. {
  123.     char *longname;
  124.     char *shortname;
  125.     struct grpconv_struct *next;
  126. } grpconv_t;
  127.  
  128. grpconv_t *glanker;                         /* Liste, nach longnames sortiert */
  129. grpconv_t *gsanker;                         /* Liste nach shortnames sort. */
  130. BOOLEAN grplist_changed;                    /* Hat sich die Liste geaendert? */
  131. grpconv_t *gshash[256];                     /* Gruppennamenhashtabelle */
  132. grpconv_t *glhash[256];                     /* Gruppennamenhashtabelle */
  133.  
  134. /* 
  135.  * Alle chars sind unsigned, weil ein paar daemliche C-Librarys meinen,
  136.  * sie duerften sonst Zeichen wegwerfen oder ruinieren. Das betraf 
  137.  * auch (aeltere) Linux Libs.
  138.  */
  139.  
  140. static unsigned char l_mausid[HEADLEN];        /* # */
  141. static unsigned char l_mausref[HEADLEN];       /* - */
  142. static unsigned char l_from[HEADLEN];          /* V */
  143. static unsigned char l_to[HEADLEN];            /* A */
  144. static unsigned char l_subject[HEADLEN];       /* W */
  145. static unsigned char l_date[HEADLEN];          /* E */
  146. static unsigned char l_groups[HEADLEN];        /* G */
  147. static unsigned char l_status[HEADLEN];        /* B */
  148. static unsigned char l_realref[HEADLEN];       /* R */
  149. static unsigned char l_organization[HEADLEN];  /* O */
  150. static unsigned char l_name[HEADLEN];          /* N */
  151. static unsigned char l_distribution[HEADLEN];  /* D */
  152. static unsigned char l_realid[HEADLEN];        /* I */
  153. static unsigned char l_gateway[HEADLEN];       /* Y */
  154.  
  155. static BOOLEAN kill_umlaute;      /* Umlaute im Betreff ersetzen */
  156. static BOOLEAN quark16;           /* Ist es eine Quark 1.6? */
  157. static BOOLEAN savekopfaskopf;    /* >-Zeilen erzeugen? */
  158. static BOOLEAN savekopfastext;    /* Oder >-Zeilen als Textzeilen? */
  159. static short showdots = 0;        /* Verlaufsanzeige */
  160. static char *fake_tversion;       /* Tauschversionsnr in LOG/HEAD faelschen? */
  161. static char *fake_version;        /* Versionsnummer in LOG/HEAD faelschen? */
  162. static char *fake_box;            /* Boxtyp im LOG/HEAD faelschen? */
  163. static unsigned int limit_subject;/* Betreff kuerzen */
  164. static BOOLEAN headfilt;          /* HEAD filtern */
  165. static FILE *dotsfile = stdout;   /* Punkte auf stdout (Default) */
  166. static unsigned int columns = 80; /* Zeichen pro BS-Zeile */
  167. static BOOLEAN gconv2long;        /* Gruppennamen Kurz->Lang */
  168. static BOOLEAN gconv2short;       /* Gruppennamen Lang->Kurz */
  169. static BOOLEAN make_shortnames;   /* Falls noetig Gruppennamen kuerzen */
  170. static BOOLEAN long2short_ids;    /* Lange IDs in Kurze wandeln */
  171. static BOOLEAN short2long_ids;    /* und kurze in lange */
  172.     /* Nachrichten mit nichtkonvertierbaren IDs wegwerfen? */
  173. static BOOLEAN discard_false_ids; 
  174. static FILE *probsfile = stderr;  /* Problemmeldungen in welches File? */
  175.  
  176. /*
  177.  * Nun doch noch: Prototypen.
  178.  */
  179.  
  180. void clean_header (void);
  181. BOOLEAN write_header (FILE * f);
  182. void * xmalloc (size_t size);
  183. void savekopf (unsigned char *s);
  184. unsigned char * nextline(FILE *in);
  185. void quarkoutfile (FILE * in, FILE * out);
  186. void usage (void);
  187. unsigned char * trim(unsigned char *s);
  188. unsigned char * get_longname(unsigned char *sname);
  189. unsigned char * get_shortname(unsigned char *lname);
  190. void grp_einfuegen(grpconv_t *snew,grpconv_t *lnew);
  191. void speichere_grplist(const char *fname);
  192. void lade_grplist(const char *fname);
  193. short convert_long_maus_id(char *shortidline,char *longidline,
  194.     char *eingabezeit);
  195.  
  196. void
  197. clean_header (void)
  198. {
  199.     kentry_t *p;
  200.     kentry_t *pnext;
  201.  
  202.     memset (l_mausid, 0, HEADLEN);
  203.     memset (l_mausref, 0, HEADLEN);
  204.     memset (l_from, 0, HEADLEN);
  205.     memset (l_to, 0, HEADLEN);
  206.     memset (l_subject, 0, HEADLEN);
  207.     memset (l_date, 0, HEADLEN);
  208.     memset (l_groups, 0, HEADLEN);
  209.     memset (l_status, 0, HEADLEN);
  210.     memset (l_realid, 0, HEADLEN);
  211.     memset (l_realref, 0, HEADLEN);
  212.     memset (l_organization, 0, HEADLEN);
  213.     memset (l_name, 0, HEADLEN);
  214.     memset (l_distribution, 0, HEADLEN);
  215.     memset (l_gateway, 0, HEADLEN);
  216.  
  217.     p = kanker;
  218.     while (p)
  219.     {
  220.         pnext = p->next;
  221.         free (p);
  222.         p = pnext;
  223.     }
  224.     kanker = NULL;
  225.     klast = NULL;
  226. }
  227.  
  228. #define MAUSDE ".maus.de"
  229. #define MAUSDE_LEN (sizeof(MAUSDE)-1)
  230.  
  231. /* Lange Message-IDs in den #- und Minuszeilen konvertieren in kurze 
  232.  * in den #- und Minuszeilen sowie lange in den I/R-Zeilen.
  233.  * Außerdem wird, um die Entstehung von Dupes bei der Vergabe neuer
  234.  * langer Message-IDs in der MAUS zu vermeiden, die Eingabezeit 
  235.  * auf den Inhalt der urspruenglichen langen ID angepaßt. Das sollte
  236.  * nicht noetig sein, aber sicher ist sicher.
  237.  */
  238. short
  239. convert_long_maus_id(char *shortidline,char *longidline,char *eingabezeit)
  240. {
  241.     char *mausde;
  242.     char *at;
  243.     char *dot;
  244.     char *p;
  245.     char c;
  246.     at=strchr(shortidline,'@');
  247.     if (!at)
  248.          return 1; /* Spezialnachricht o.ae. */
  249.     dot=strchr(shortidline,'.');
  250.     if (!dot || dot>at)
  251.         return 0;
  252.     c=toupper(dot[1]);
  253.     if (!isupper(c))
  254.         return 0;
  255.  
  256.     mausde=shortidline+strlen(shortidline)-MAUSDE_LEN;
  257.     if (strcasecmp(mausde,MAUSDE))
  258.         return 0;
  259.  
  260.     /* .maus.de muss mindestens zwei Zeichen nach dem @ kommen */
  261.     if (mausde<at+2)
  262.         return 0;
  263.  
  264.     /* 
  265.      * Das ist eine lange MAUS-ID. Sie hat das Format:
  266.      *    199406250722.a10693@du.maus.de
  267.      *                ^dot   ^at^mausde
  268.      * Zuerst mal in die Zeile fuer die lange ID kopieren, falls 
  269.      * erwuenscht. Sinnvollerweise wuerde das *immer* gemacht, aber
  270.      * MAUS laeßt Fremdboxen zur Zeit keine I-Zeilen schicken.
  271.      */
  272.     if (longidline)
  273.         strcpy(longidline,shortidline);
  274.     *mausde='\0'; /* .maus.de weg */
  275.  
  276.     /* -> 199406250722.a10693@du */
  277.     p=at;
  278.     while (*p)
  279.         *p++=toupper(*p);
  280.  
  281.     /* -> 199406250722.a10693@DU */
  282.     *dot='\0';
  283.     if (eingabezeit)
  284.         strcpy(shortidline,eingabezeit);
  285.  
  286.     /* -> 199406250722\0a10693@DU */
  287.  
  288.     *shortidline=c;
  289.  
  290.     /* -> A99406250722.a10693@DU */
  291.  
  292.     memmove(shortidline+1,dot+2,strlen(dot+2)+1);
  293.  
  294.     /* -> A10693@DU */
  295.  
  296.     return TRUE; /* gelungen */
  297. }
  298.  
  299. /*
  300.  * Schreibt die Headerdaten in das File.
  301.  * liefert TRUE, falls die Nachricht weiterhin geschrieben werden,
  302.  * und FALSE, wenn sie Richtung /dev/null wandern soll.
  303.  */
  304. BOOLEAN
  305. write_header (FILE * f)
  306. {
  307.     /* Falls keine lange ID vorhanden ist und die Nachricht aus dem
  308.      * MausNet ist, dann eine lange ID erzeugen.
  309.      */
  310.     if (!l_realid[0] && l_date[0] && strchr (l_mausid,'@'))
  311.     {
  312.         char *p;
  313.         p=strchr(l_mausid,'@');
  314.         if (p && !strchr(p,'.'))
  315.         {
  316.             p=strchr(l_from,'@');
  317.             if (p && !strchr(p,'.'))
  318.             {
  319.                 sprintf(l_realid,"%s.%s.maus.de",l_date,l_mausid);
  320.                 p=l_realid;
  321.                 while (*p)
  322.                     *p++=tolower(*p);
  323.             }
  324.         }
  325.     }
  326. #define OUTPUT(x,y) if (y[0]) fprintf(f,"%c%s"CRLF,x,y);
  327.     if (long2short_ids)
  328.     {
  329.         /* 
  330.          * Lange IDs in kurze wandeln: Ein primitiver USE2MAUS, der
  331.          * mit brutaler Gewalt aus langen IDs in der "#"-Zeile kurze
  332.          * macht und die lange in die I-Zeile kopiert.
  333.          */
  334.         if (!convert_long_maus_id(l_mausid,NULL,l_date))
  335.         {
  336.             /* konnte nicht konvertiert werden? */
  337.             fprintf(probsfile,
  338.                 "Kann lange ID nicht in kurze konvertieren: %s\n",
  339.                 l_mausid);
  340.             if (discard_false_ids)
  341.                 return FALSE;
  342.         }
  343.         if (*l_mausref && !convert_long_maus_id(l_mausref,l_realref,NULL))
  344.         {
  345.             /* Das ist normal */
  346.             strcpy(l_realref,l_mausref);
  347.             *l_mausref='\0';
  348.         }
  349.         OUTPUT ('#', l_mausid);
  350.         OUTPUT ('-', l_mausref);
  351.         OUTPUT ('R', l_realref);
  352.         OUTPUT ('E', l_date);
  353.     }
  354.     else if (short2long_ids && strchr(l_mausid,'@'))
  355.     {
  356.         /* 
  357.          * Kurze IDs verwerfen, um lange einzusetzen.
  358.          * Eine lange ID haben wir schon!
  359.          */
  360.         if (!strstr(l_realid,MAUSDE) && !*l_gateway)
  361.         {
  362.             char *p=strchr(l_mausid,'@');
  363.             sprintf(l_gateway,"Gateway @ %s",p+1);
  364.         }
  365.         strcpy(l_mausid,l_realid);
  366.         *l_realid='\0';
  367.         strcpy(l_mausref,l_realref);
  368.         *l_realref='\0';
  369.         OUTPUT ('#', l_mausid);
  370.         OUTPUT ('-', l_mausref);
  371.         OUTPUT ('E', l_date);
  372.     }
  373.     else
  374.     {
  375.         OUTPUT ('#', l_mausid);
  376.         OUTPUT ('I', l_realid);
  377.         OUTPUT ('-', l_mausref);
  378.         OUTPUT ('R', l_realref);
  379.         OUTPUT ('E', l_date);
  380.     }
  381.  
  382.     OUTPUT ('D', l_distribution);
  383.     OUTPUT ('Y', l_gateway);
  384.     OUTPUT ('O', l_organization);
  385.     OUTPUT ('V', l_from);
  386.     OUTPUT ('N', l_name);
  387.     OUTPUT ('A', l_to);
  388.     if (l_subject[0]) 
  389.     {
  390.         /* fuer suboptimal programmierte Frontends, die nicht mit langen
  391.          * Betreffs umgehen koennen.
  392.          */
  393.         if (limit_subject && strlen(l_subject)>limit_subject)
  394.             l_subject[limit_subject]='\0';
  395.         if (!kill_umlaute)
  396.             fprintf(f,"W%s"CRLF,l_subject);
  397.         else
  398.         {
  399.             /* Und das ist fuer ebenfalls suboptimal programmierte 
  400.              * Frontends, die keine Umlaute im Betreff moegen.
  401.              */
  402.             unsigned char *p;
  403.             unsigned int count=0;
  404.             fputc('W',f);
  405.             p=l_subject;
  406.             while (*p)
  407.             {
  408.                 switch(*p)
  409.                 {
  410.                 /* ### DANGER: Umlaute bei Portierung wandeln! */
  411.                 case 'ü': fputs("ue",f); count+=2; break;
  412.                 case 'ö': fputs("oe",f); count+=2; break;
  413.                 case 'ä': fputs("ae",f); count+=2; break;
  414.                 case 'Ü': fputs("Ue",f); count+=2; break;
  415.                 case 'Ö': fputs("Oe",f); count+=2; break;
  416.                 case 'Ä': fputs("Ae",f); count+=2; break;
  417.                 case 'ß': fputs("ss",f); count+=2; break;
  418.                 default: fputc(*p,f); count++; break;
  419.                 }
  420.                 p++;
  421.                 if (limit_subject && count>=limit_subject)
  422.                     break;
  423.             }
  424.             fputs(CRLF,f);
  425.         }
  426.     }
  427.     OUTPUT ('G', l_groups);
  428.     OUTPUT ('B', l_status);
  429.     if ((savekopfaskopf || savekopfastext) && kanker)
  430.     {
  431.         kentry_t *p;
  432.  
  433.         p = kanker;
  434.         while (p)
  435.         {
  436.             if (savekopfaskopf)
  437.             {
  438.                 OUTPUT ('>', p->line);
  439.             }
  440.             else
  441.             {
  442.                 OUTPUT (':', p->line);
  443.             }
  444.             p = p->next;
  445.         }
  446.         if (savekopfaskopf)
  447.             fprintf (f, ">" CRLF);
  448.         else
  449.             fprintf (f, ":" CRLF);
  450.     }
  451. #undef OUTPUT
  452.     return TRUE;
  453. }
  454.  
  455. /*
  456.  * sicheres malloc
  457.  */
  458. void *
  459. xmalloc (size_t size)
  460. {
  461.     void *p;
  462.  
  463.     p = malloc (size);
  464.     if (!p)
  465.     {
  466.         /* Nichts zu machen, Pech */
  467.         fprintf (stderr, "%s: out of memory\n", myname);
  468.         exit (1);
  469.     }
  470.     return p;
  471. }
  472.  
  473. /*
  474.  * Kopfzeile sichern
  475.  */
  476. void
  477. savekopf (unsigned char *s)
  478. {
  479.     kentry_t *p;
  480.  
  481.     p = xmalloc (sizeof (kentry_t));
  482.  
  483.     strcpy (p->line, s);
  484.     p->next = NULL;
  485.     if (!klast)
  486.     {
  487.         kanker = p;
  488.         klast = p;
  489.     }
  490.     else
  491.     {
  492.         klast->next = p;
  493.         klast = p;
  494.     }
  495. }
  496.  
  497. unsigned char *inputbuffer;
  498.  
  499. unsigned char *
  500. nextline(FILE *in)
  501. {
  502.     unsigned char *p;
  503. #ifdef HAVE_MMAP
  504.     if (is_mmapped)
  505.     {
  506.         unsigned char *ret;
  507.         ret=inputbuffer;
  508.         if (!ret)
  509.             return NULL;
  510.         p=strpbrk(inputbuffer,"\r\n");
  511.         if (!p)
  512.         {
  513.             /* Möglicherweise Müll in der Zeile */
  514.             /* ASCII-Null *in* der Zeile kommt sehr, sehr schlecht */
  515.             while (1)
  516.             {
  517.                 p=inputbuffer+strlen(inputbuffer);
  518.                 if (p!=(unsigned char *)(mmaddr+mmsize-1))
  519.                     *p=' ';
  520.                 else break;
  521.             }
  522.             p=strpbrk(inputbuffer,"\r\n");
  523.         }
  524.         if (p)
  525.         {
  526.             if (*p=='\r')
  527.             {
  528.                 *p++='\0';
  529.                 *p++='\0';
  530.             }
  531.             else
  532.                 *p++='\0';
  533.             if (p<=((unsigned char *)mmaddr+mmsize-1))
  534.                 inputbuffer=p;
  535.             else
  536.                 inputbuffer=NULL;
  537.         }
  538.         else
  539.             inputbuffer=NULL;
  540.         if (*ret)
  541.             return ret;
  542.         return NULL;
  543.     }
  544. #endif
  545.     if (!fgets (inputbuffer, LINESIZE, in))
  546.         return NULL;
  547.     p=strpbrk(inputbuffer,"\r\n");
  548.     if (p)
  549.         *p='\0';
  550.     return inputbuffer;
  551. }
  552.  
  553. /*
  554.  *    Die Funktion, die die ganze Arbeit erledigt
  555.  */
  556. void
  557. quarkoutfile (FILE * in, FILE * out)
  558. {
  559.     unsigned char *line;
  560.     unsigned char *p;
  561.     BOOLEAN is_header;                       /* Sind wir im Header einer Nachricht? */
  562.     enum
  563.     {
  564.         KEIN_SPECIAL,                   /* in normaler Nachricht */
  565.         SOME_SPECIAL,                   /* in irgendeiner Spezialnachricht */
  566.         ITG,                           /* im ITG */
  567.         HEAD,                           /* im HEAD */
  568.         LOG                            /* im LOG */
  569.     }
  570.     in_special = KEIN_SPECIAL;
  571.     int logcmdwritten;                   /* :#CMD in #LOG eingefuegt? */
  572.     int killdoublePdoubleC;               /* naechste :#-Zeile loeschen */
  573.     int nachrichtennr;                   /* Nummer der aktuellen Nachricht */
  574.     BOOLEAN work_on_it=TRUE;           /* Diese Nachricht sichern? */
  575.  
  576. #ifdef HAVE_MMAP
  577.     if (is_mmapped)
  578.         inputbuffer = (unsigned char *) mmaddr;
  579.     else
  580. #endif
  581.         inputbuffer = xmalloc (LINESIZE);
  582.  
  583.     clean_header ();
  584.     is_header = FALSE;
  585.     logcmdwritten = 0;
  586.     killdoublePdoubleC = 0;
  587.     nachrichtennr = 0;
  588.  
  589.     /* Solange es noch Daten zu lesen gibt */
  590.     while ((line=nextline(in))!=NULL)
  591.     {
  592.         if (!work_on_it)
  593.         {
  594.             if (*line!='#')
  595.                 continue;
  596.             work_on_it=TRUE;
  597.         }
  598.         /* Sind wir in einer Spezialnachricht (LOG oder Ixx)=? */
  599.         if (in_special != KEIN_SPECIAL)
  600.         {
  601.             /*
  602.              *    Ja. In dem Fall wird die Nachricht einfach kopiert.
  603.              *  Um Header etc. braucht man sich nicht zu kuemmern.
  604.              */
  605.             /* wenn nicht schon die naechste Nachricht beginnt */
  606.             if (line[0] != '#')
  607.             {
  608.                 /* Ueber Braindamages bei Frontendprogrammierern 
  609.                  * koennte ich lange lametieren. Lassen wir das besser.
  610.                  * Zuerst der HEAD-Filter (manche Frontends moegen HEAD
  611.                  * nicht).
  612.                  */
  613.                 if (headfilt && in_special==HEAD)
  614.                      continue;
  615.  
  616.                 /* Manche fehlerhafte Frontends, so z.B. Cat 2.6, fragen
  617.                  * die Versionsnummer der Box ab, um festzustellen, ob
  618.                  * bestimmte Features vorhanden sind. Das ist, da die
  619.                  * Versionsnummern sich natuerlich bei unterschiedlichen
  620.                  * Boxtypen unterscheiden, ausgesprochen unguenstig.
  621.                  * Wer soetwas tut, sollte eigentlich mit Schlaegen oder
  622.                  * Benutzerflucht bestraft werden.
  623.                  * Leider wird das aber nicht passieren, und daher bietet
  624.                  * Quarkoutfile Abhilfe: Es kann den Boxtyp, die 
  625.                  * Boxversionsnummer und die Tauschversionsnummer 
  626.                  * faelschen
  627.                  */
  628.                 if (fake_version || fake_box || fake_tversion)
  629.                 {
  630.                     if (in_special == LOG)
  631.                     {
  632.                         /* Tauschversionsnummer steht in !V und %T */
  633.                         if (fake_tversion && !strncmp (line, ":!V", 3))
  634.                         {
  635.                             fprintf (out, ":!V%s" CRLF, fake_tversion);
  636.                             continue;    /* naechste Zeile bitte */
  637.                         }
  638.                         else if (fake_tversion && !strncmp (line, ":%T", 3))
  639.                         {
  640.                             fprintf (out, ":%%T%s" CRLF, fake_tversion);
  641.                             continue;    /* naechste Zeile bitte */
  642.                         }
  643.                         /* Boxversionsnummer steht in !M und %V */
  644.                         if (fake_version && !strncmp (line, ":!M", 3))
  645.                         {
  646.                             fprintf (out, ":!M%s" CRLF, fake_version);
  647.                             continue;    /* naechste Zeile bitte */
  648.                         }
  649.                         else if (fake_version && !strncmp (line, ":%V", 3))
  650.                         {
  651.                             fprintf (out, ":%%V%s" CRLF, fake_version);
  652.                             continue;    /* naechste Zeile bitte */
  653.                         }
  654.                         /* Boxtyp in %B */
  655.                         else if (fake_box && !strncmp (line, ":%B", 3))
  656.                         {
  657.                             fprintf (out, ":%%B%s" CRLF, fake_box);
  658.                             continue;    /* naechste Zeile bitte */
  659.                         }
  660.                     }
  661.                     else if (in_special == HEAD)
  662.                     {
  663.                         if (fake_version && !strncmp (line, ":V", 3))
  664.                         {
  665.                             fprintf (out, ":V%s" CRLF, fake_version);
  666.                             continue;
  667.                         }
  668.                         if (fake_tversion && !strncmp (line, ":T", 3))
  669.                         {
  670.                             fprintf (out, ":T%s" CRLF, fake_tversion);
  671.                             continue;
  672.                         }
  673.                         if (fake_box && !strncmp (line, ":B", 3))
  674.                         {
  675.                             fprintf (out, ":B%s" CRLF, fake_box);
  676.                             continue;
  677.                         }
  678.                     }
  679.                 }
  680.  
  681.                 /* Soviel zur Versionsnummer, nun zur Konvertierung der
  682.                  * IDs im LOG. Das gestaltet sich schwieriger, da die
  683.                  * Quark 1.x die langen IDs nicht im #LOG zurueckmeldet
  684.                  * (da sie sie auch gar nicht zur MAUS schicken darf und
  685.                  * von daher nicht weiss, welche ID die MAUS tatsaechlich
  686.                  * dafuer einsetzt).
  687.                  */
  688.                 if (in_special == LOG && short2long_ids)
  689.                 {
  690.                     static char last_gleich[HEADLEN]="";
  691.                     char c=line[1];
  692.                     /* dann im LOG IDs konvertieren */
  693.                     if (c=='"' || c=='#' || c=='$')
  694.                     {
  695.                         if (*last_gleich)
  696.                         {
  697.                             fprintf(out,"%s"CRLF,last_gleich);
  698.                             *last_gleich='\0';
  699.                         }
  700.                     }
  701.                     if (!strncmp(line,":=",2))
  702.                     {
  703.                         /* sicherheitshalber aufbewahren */
  704.                         strcpy(last_gleich,line);
  705.                         continue;
  706.                     }
  707.                     if (!strncmp(line,":!=",3))
  708.                     {
  709.                         fprintf(out,":=%s"CRLF,line+3);
  710.                         *last_gleich='\0';
  711.                         continue;
  712.                     }
  713.                 }
  714.  
  715.                 /* nun zum Fehlen der #CMD-Bestaetigung in der Quark. 
  716.                  * Braindamage (c) Uwe Ohse.
  717.                  */
  718.                 if (in_special == LOG && !logcmdwritten)
  719.                 {
  720.                     if (!strcmp (line, ":#CMD"))
  721.                     {
  722.                         fprintf (out, "%s" CRLF, line);
  723.                         logcmdwritten = 1;
  724.                         continue;
  725.                     }
  726.                     if (line[1] == '\"' || line[1] == '$')
  727.                     {
  728.                         fprintf (out, ":#CMD" CRLF);
  729.                         fprintf (out, ":#" CRLF);
  730.                         fprintf (out, "%s" CRLF, line);
  731.                         logcmdwritten = 1;
  732.                         killdoublePdoubleC = 1;        /* muss :# wegwerfen */
  733.                         continue;
  734.                     }
  735.                 }
  736.                 if (in_special == LOG && killdoublePdoubleC &&
  737.                     !strcmp (line, ":#"))
  738.                 {
  739.                     /* genau diese Zeile loeschen! */
  740.                     continue;
  741.                 }
  742.  
  743.                 if (in_special == ITG)
  744.                 {
  745.                     unsigned char *p;
  746.                     /* Leerzeichen am Ende der G-Zeilen loeschen */
  747.                     if (*line == 'G' || (*line == ':' && *(line + 1) == 'G'))
  748.                     {
  749.                         unsigned char *c = line + strlen (line) - 1;
  750.                         unsigned char *compare;
  751.  
  752.                         while (*c == ' ')
  753.                             *c-- = '\0';
  754.  
  755.                         if (*line==':')
  756.                             compare=line+1;
  757.                         else
  758.                             compare=line;
  759.  
  760.                         if (gconv2short && (p=get_shortname(compare))!=NULL)
  761.                         {
  762.                             fprintf (out, ":G%s" CRLF, p);
  763.                             continue;
  764.                         }
  765.                         else if (gconv2long && (p=get_longname(compare))!=NULL)
  766.                         {
  767.                             fprintf (out, ":G%s" CRLF, p);
  768.                             continue;
  769.                         }
  770.                     }
  771.                     if (*line == 'G' || *line == 'U' || *line == 'C' || *line == 'F')
  772.                         fprintf (out, ":%s" CRLF, line);
  773.                     else
  774.                         fprintf (out, "%s" CRLF, line);
  775.                     continue;
  776.                 }
  777.                 fprintf (out, "%s" CRLF, line);
  778.                 continue;
  779.             }
  780.             /* Eine neue Nachricht - specialflag zuruecksetzen */
  781.         }
  782.  
  783.         /*
  784.          *    Der eigentliche Parser:
  785.          */
  786.         switch (line[0])
  787.         {
  788.         case '#':                /* ID-Zeile, Beginn einer neuen Nachricht */
  789.             /*
  790.              *    Was muss getan werden?
  791.              *  - Wenn der Header noch nicht gesichert wurde (was definitiv
  792.              *    bei Statusnachrichten so ist), dann den Header wegschreiben.
  793.              *  - die Headerdaten muessen geloescht werden.
  794.              *  - Bei Spezialnachrichten (Ixx,Jxx,LOG etc) Kopiermodus
  795.              *    einschalten.
  796.              *  - Wir muessen uns merken, dass wir wieder im Header sind.
  797.              *  - und natuerlich die ID kopieren!
  798.              */
  799.  
  800.             /* Falls noch im Header (warum auch immer), dann Header sichern */
  801.             if (is_header)
  802.                 (void) write_header (out);
  803.             work_on_it=TRUE;
  804.             /* alte Headerdaten loeschen */
  805.             clean_header ();
  806.  
  807.             in_special = KEIN_SPECIAL;
  808.  
  809.             /* Verlaufsanzeige */
  810.             if (showdots && nachrichtennr % showdots == 0)
  811.             {
  812.                 static int dotcount = 0;
  813.  
  814.                 fputc ('.', dotsfile);
  815.                 if (++dotcount % columns == 0)
  816.                     fprintf (dotsfile, CRLF);
  817.                 fflush(dotsfile);
  818.             }
  819.  
  820.             /* wenn die ID keinen @ enthaelt */
  821.             if (!strchr (line + 1, '@'))
  822.             {
  823.                 /*
  824.                  *    dann ist es eine Spezialmitteilung
  825.                  */
  826.                 in_special = SOME_SPECIAL;
  827.                 if (!strcmp (line, "#ITG"))
  828.                     in_special = ITG;
  829.                 if (!strcmp (line, "#HEAD"))
  830.                     in_special = HEAD;
  831.                 if (!strcmp (line, "#LOG"))
  832.                     in_special = LOG;
  833.                 /* HEAD auf Wunsch filtern */
  834.                 if (in_special!=HEAD || !headfilt)
  835.                     fprintf (out, "%s" CRLF, line);
  836.                 killdoublePdoubleC = 0;
  837.                 logcmdwritten = 0;
  838.                 continue;
  839.             }
  840.             /* ID kopieren */
  841.             strcpy (l_mausid, line + 1);
  842.             /* wir sind im Header einer Nachricht */
  843.             is_header = TRUE;
  844.             break;
  845.         case '-':                /* Verkettungs-ID */
  846.             strcpy (l_mausref, line + 1);
  847.             break;
  848.         case 'V':                /* Von */
  849.             strcpy (l_from, line + 1);
  850.             break;
  851.         case 'A':                /* an */
  852.             strcpy (l_to, line + 1);
  853.             break;
  854.         case 'Y':                /* gateway */
  855.             strcpy (l_gateway, line + 1);
  856.             break;
  857.         case 'W':                /* Wegen */
  858.             /*
  859.              *    Die Quark verkuerzt leider auch das Subject um zwei Zeichen.
  860.              *  Das echte Subject geht leider verloren, und daher kann auch
  861.              *  quarkoutfile nichts dagegen tun. Bei richtig langen
  862.              *  Betreffs kommt eine ">Wg. :"-Zeile, aus der man den 
  863.              *  Betreff rekonstruieren kann - aber leider nicht bei 
  864.              *  Betreffs mit 29 und 30 Zeichen. Pech! Quark 1.5i4
  865.              *  macht es besser :-)
  866.              */
  867.             strcpy (l_subject, line + 1);
  868.             break;
  869.         case 'E':                /* Eingabezeit */
  870.             /* Bei Quark leider Datum des Einfuegens in die DB */
  871.             strcpy (l_date, line + 1);
  872.             break;
  873.         case 'G':                /* Gruppe */
  874.             /*
  875.              *  Die Gruppennamen bei Quarks sind etwas laenger (16 Zeichen),
  876.              *  und das bringt einige Frontends in leichte Probleme (z.B.
  877.              *  MAUTAU (considered dead) und auch fruehere Versionen von
  878.              *  Catputz 2.0x (heutige sind clean).
  879.              */
  880.             if (gconv2long && (p=get_longname(line+1))!=NULL)
  881.                 strcpy(l_groups,p);
  882.             else if (gconv2short && (p=get_shortname(line+1))!=NULL)
  883.                 strcpy(l_groups,p);
  884.             else
  885.                 strcpy (l_groups, line + 1);
  886.             break;
  887.         case 'B':                /* Bearbeitungsstatus */
  888.             /*
  889.              *  Kommt bei Quark 1.x nur in PMs, Statusmeldungen verschickt
  890.              *  sie nicht.
  891.              */
  892.             strcpy (l_status, line + 1);
  893.             break;
  894.         case 'I':                /* ID - die echte ID aus Fremdnetzen */
  895.             /*
  896.              *  Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
  897.              *  aber dennoch ankommen sollte, merken wir sie uns natuerlich.
  898.              */
  899.             strcpy (l_realid, line + 1);
  900.             break;
  901.         case 'R':                /* echte References aus Fremdnetzen */
  902.             /*
  903.              *  Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
  904.              *  aber dennoch ankommen sollte, merken wir sie uns natuerlich.
  905.              */
  906.             strcpy (l_realref, line + 1);
  907.             break;
  908.         case 'N':                /* Realname aus Fremdnetzen */
  909.             /*
  910.              *  Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
  911.              *  aber dennoch ankommen sollte, merken wir sie uns natuerlich.
  912.              */
  913.             strcpy (l_name, line + 1);
  914.             break;
  915.         case 'O':                /* Organization aus Fremdnetzen */
  916.             /*
  917.              *  Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
  918.              *  aber dennoch ankommen sollte, merken wir sie uns natuerlich.
  919.              */
  920.             strcpy (l_organization, line + 1);
  921.             break;
  922.         case 'D':                /* Distribution */
  923.             /*
  924.              *  Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
  925.              *  aber dennoch ankommen sollte, merken wir sie uns natuerlich.
  926.              *  btw: Diese Zeile ist bis heute in keiner Tauschdoku
  927.              *  erwaehnt. Soviel zu deren Aktualitaet.
  928.              */
  929.             strcpy (l_distribution, line + 1);
  930.             break;
  931.         case '>':                /* Kopfzeilen */
  932.             /*
  933.              *  Die Quark lieferte diese Zeilen vor Version 1.6 nicht.
  934.              *  In Quark 1.6 sind sie allerdings nicht brauchbarer als die
  935.              *  normalen Headerzeilen, deshalb ignoriert Quarkoutfile sie.
  936.              */
  937.             savekopf (line + 1);
  938.             if (!quark16 && is_header)
  939.             {
  940.                 work_on_it=write_header (out);
  941.                 is_header = FALSE;
  942.             }
  943.             break;
  944.         case ':':                /* Textzeilen */
  945.             /*
  946.              *  hier beginnt die eigentliche Arbeit, das konvertieren
  947.              *  gewisser Textzeilen.
  948.              */
  949.  
  950.             /* wenn nicht im Header, dann Zeile einfach wegschreiben */
  951.             if (!is_header)
  952.             {
  953.                 fprintf (out, "%s" CRLF, line);
  954.                 break;
  955.             }
  956.             /*
  957.              *  So, wir sind definitiv im Header einer Nachricht. Ausserdem
  958.              *  steht fest, dass es das Outfile einer Quark ist (bei MAUS
  959.              *  kommen >-Zeilen, die (s.o.) den Header beenden).
  960.              *  Das heisst, es geht los. Zum Beispiel koennte im Outfile
  961.              *  stehen:
  962.              *      :Von : loginname@some.world (Mo, 11.10.93 10:27)
  963.              *      :Name: Real Name
  964.              *      :Box : irgendeine site oder organisation
  965.              *      :MId : <29b8ufINN1ou@some.world>
  966.              *      :RId : <A27961@someother.world>
  967.              *      :
  968.              *  Auf die Reihenfolge der Zeilen verlassen wir uns natuerlich
  969.              *  nicht.
  970.              *  Der Parser ist etwas unschoen, da es verschiedene Formen
  971.              *  fuer die Kopfzeilen gibt. Zum Beispiel tritt ":Von :" als
  972.              *  als ": Von:" auf.
  973.              */
  974.  
  975.             /* VON-Zeile? */
  976.             if (strncmp (line + 1, "Von : ", 6) == 0
  977.                 || strncmp (line + 1, " Von: ", 6) == 0)
  978.             {
  979.                 /*
  980.                  *    Zerlege
  981.                  *      :Von : loginname@some.world (Mo, 11.10.93 10:27)
  982.                  *  in  "loginname@some.world" -> l_from
  983.                  *  und "Mo, 11.10.93 10:27"   -> l_date
  984.                  *  (Anfuehrungszeichen nur zur Erlaeuterung)
  985.  
  986.                  *  Der Absender wird nach l_from kopiert, da l_from bei
  987.                  *  Quarks ab Zeichen 25 abgeschniten wird. Dies ist
  988.                  *  durch die veraltete Quark-Datenbank bedingt.
  989.                  */
  990.                 unsigned char *uhr;
  991.                 unsigned char *p;
  992.  
  993.                 savekopf (line + 1);
  994.  
  995.                 /* Zeile in Absender und Uhrzeit splitten */
  996.                 uhr = strrchr (line, '(');
  997.                 if (uhr)
  998.                     p = strchr (uhr, ')');
  999.                 if (uhr && p)
  1000.                 {
  1001.                     p = uhr - 1;
  1002.                     *(uhr++) = '\0';    /* '(' weg */
  1003.                     /*
  1004.                      *    Die schliessende Klammer und Leerzeichen am Ende des
  1005.                      *  Datums weg
  1006.                      */
  1007.                     p = uhr + strlen (uhr) - 1;
  1008.                     while (*p == ')' || *p == ' ')
  1009.                         *(p--) = '\0';
  1010.                     /*
  1011.                      *    Uhrzeit nach l_date uebertragen. Das bedingt eine
  1012.                      *  Formatkonvertierung: Aus
  1013.                      *      "Mo, 11.10.93 10:27"
  1014.                      *                 11111111 }
  1015.                      *       012345678901234567 } Offset
  1016.                      *  wird
  1017.                      *      "199310111027"
  1018.                      */
  1019.                     sprintf (l_date, "19%2.2s%2.2s%2.2s%2.2s%2.2s",
  1020.                              uhr + 10, uhr + 7, uhr + 4, uhr + 13, uhr + 16);
  1021.                 }                /* if (uhr)  (wenn Klammer und damit Uhrzeit gefunden) */
  1022.                 else if (uhr)
  1023.                     *uhr = '\0';
  1024.                 /*
  1025.                  *    und nun ueberfluessige Leerzeichen am Ende des
  1026.                  *  Absenders weg.
  1027.                  */
  1028.                 p = line + strlen (line) - 1;
  1029.                 while (*p == ' ')
  1030.                     *(p--) = '\0';
  1031.                 /*
  1032.                  *    Jetzt um den Absender kuemmern. Format nun:
  1033.                  *      Von : loginname@some.world
  1034.                  *      0123456 == offset
  1035.                  *  Problem: Diese Zeile ist in Quark 1.6 ohne @ bei lokalen
  1036.                  *  Absendern.
  1037.                  */
  1038.                 if (!quark16 || strchr (line + 7, '@'))
  1039.                     strcpy (l_from, line + 7);
  1040.             }                    /* if "Von : " oder " Von: " am Anfang */
  1041.             /*
  1042.              * oder Namens-Zeile?
  1043.              */
  1044.             else if (strncmp (line + 1, "Name: ", 6) == 0)
  1045.             {
  1046.                 /* einfacher Fall */
  1047.                 savekopf (line + 1);
  1048.                 strcpy (l_name, line + 7);
  1049.             }
  1050.             /*
  1051.              * Oder Organisations/Boxzeile?
  1052.              */
  1053.             else if (strncmp (line + 1, "Box : ", 6) == 0
  1054.                      || strncmp (line + 1, " Box: ", 6) == 0)
  1055.             {
  1056.                 savekopf (line + 1);
  1057.                 strcpy (l_organization, line + 7);
  1058.             }
  1059.             /*
  1060.              * echte Message-ID?
  1061.              */
  1062.             else if (strncmp (line + 1, "MId : ", 6) == 0
  1063.                      || strncmp (line + 1, " MId: ", 6) == 0)
  1064.             {
  1065.                 /*
  1066.                  * eingehendes Format: "Mid : <irgendwas@irgendwo>"
  1067.                  * In der I-Zeile muss aber "Iirgendwo@irgendwo" stehen,
  1068.                  * also muessen die kleiner/groesser-Zeichen weg.
  1069.                  */
  1070.                 unsigned char *p;
  1071.  
  1072.                 savekopf (line + 1);
  1073.  
  1074.                 strcpy (l_realid, line + 8);    /* <-Zeichen ueberspringen */
  1075.                 p = l_realid + strlen (l_realid) - 1;
  1076.                 if (*p == '>')
  1077.                     *p = '\0';
  1078.             }
  1079.             /*
  1080.              * oder echte (==Fremdnetz-) Message-ID?
  1081.              */
  1082.             else if (strncmp (line + 1, "RId : ", 6) == 0
  1083.                      || strncmp (line + 1, " RId: ", 6) == 0)
  1084.             {
  1085.                 /*
  1086.                  * auch hier muessen die kleiner/groesser-Zeichen weg
  1087.                  */
  1088.                 unsigned char *p;
  1089.  
  1090.                 savekopf (line + 1);
  1091.  
  1092.                 strcpy (l_realref, line + 8);
  1093.                 p = l_realref + strlen (l_realref) - 1;
  1094.                 if (*p == '>')
  1095.                     *p = '\0';
  1096.             }
  1097.             /*
  1098.              * Fido-Misfeature: An-Zeile
  1099.              */
  1100.             else if (strncmp (line + 1, "An  : ", 6) == 0
  1101.                      || strncmp (line + 1, "  An: ", 6) == 0)
  1102.             {
  1103.                 /* Die An:-Zeile ist in Quark 1.6 IMO broken, da sie kein
  1104.                  * @-Zeichen enthaelt.
  1105.                  */
  1106.                 savekopf (line + 1);
  1107.                 if (!quark16 || strchr (line + 7, '@'))
  1108.                     strcpy (l_to, line + 7);
  1109.             }
  1110.             /*
  1111.              * oder Distributionszeile?
  1112.              */
  1113.             else if (strncmp (line + 1, "Dist: ", 6) == 0)
  1114.             {
  1115.                 /* Kein savekopf (line + 1), weil "Dist:" nur ein
  1116.                  * Quark-1.5i3-Hack ist.
  1117.                  */
  1118.                 p = line + 6;
  1119.                 while (isspace (*p))
  1120.                     p++;
  1121.                 if (!*l_distribution)
  1122.                 {
  1123.                     if (!strcasecmp (p, "Net"))
  1124.                         strcpy (l_distribution, "N");
  1125.                     else if (!strcasecmp (p, "MausNet"))
  1126.                         strcpy (l_distribution, "M");
  1127.                     else if (!strcasecmp (p, "Lokal"))
  1128.                         strcpy (l_distribution, "L");
  1129.                     else
  1130.                         strcpy (l_distribution, p);
  1131.                 }
  1132.             }
  1133.             /* 
  1134.              * langer Betreff?
  1135.              */
  1136.             else if (strncmp (line + 1, "Wg. : ", 6) == 0
  1137.                      || strncmp (line + 1, " Wg.: ", 6) == 0)
  1138.             {
  1139.                 savekopf (line + 1);
  1140.                 strcpy (l_subject, line + 7);
  1141.             }
  1142.             /* 
  1143.              * Gatewaykennzeichnung?
  1144.              */
  1145.             else if (strncmp (line + 1, "Gate: ", 6) == 0
  1146.                      || strncmp (line + 1, "Gate: ", 6) == 0)
  1147.             {
  1148.                 savekopf (line + 1);
  1149.                 if (!*l_gateway)
  1150.                     strcpy (l_gateway, line + 7);
  1151.             }
  1152.             /*
  1153.              * Alles andere kann nicht mehr zum Header gehoeren.
  1154.              */
  1155.             else
  1156.             {
  1157.                 work_on_it = write_header (out);
  1158.                 if (line[1] != '\0')
  1159.                     fprintf (out, "%s" CRLF, line);
  1160.  
  1161.                 is_header = FALSE;
  1162.             }
  1163.             break;
  1164.         }                        /* switch */
  1165.     }                            /* while */
  1166.     if (showdots && nachrichtennr > showdots)
  1167.         fputc ('\n', dotsfile);
  1168. }
  1169.  
  1170. void
  1171. usage (void)
  1172. {
  1173.     fprintf (stderr, "Benutzung: quarkoutfile [Optionen]\n");
  1174.     fprintf (stderr,
  1175.         "  Erlaubte Optionen:\n"
  1176.         "    -16             (Outfile von Quark 1.6 konvertieren)\n"
  1177.         "    -b bufsiz       (in Kilobyte, fuer Ein- und Ausgabe)\n"
  1178.         "    -B Boxtyp       (Boxtyp faelschen, z.B. '-B MAUS')\n"
  1179.         "    -d Msgs/Punkt   (Auf 'n' Nachrichten einen Punkt ausgeben\n"
  1180.         "    -F Boxversion   (Boxversion faelschen, z.B. '-F 7.94')\n"
  1181.         "    -g gruppen.cnf  (Gruppennamen Lang->Kurz)\n"
  1182.         "    -G gruppen.cnf  (Gruppennamen Kurz->Lang)\n"
  1183.         "    -I              (Infile bearbeiten)\n"
  1184.         "    -h              (dieser Text)\n"
  1185.         "    -H              (HEAD filtern)\n"
  1186.         "    -K              (Kopfzeilen erzeugen)\n"
  1187.         "    -l              (Lange IDs in Kurze wandeln (quark2maus)\n"
  1188.         "    -L              (Kurze IDs in Lange wandeln (maus2quark)\n"
  1189.         "    -o outputfile   (und in diese Schreiben)\n"
  1190.         "    -S Zeichen      (Betreff auf 'n' Zeichen kuerzen)\n"
  1191.         "    -T              (Textzeilen anstelle der Kopfzeilen)\n"
  1192.         "    -U              (Umlaute im Betreff ersetzen)\n"
  1193.         "    -v              (Ausgabe der Versionsnummer?)\n"
  1194.         );
  1195. }
  1196.  
  1197. unsigned char *
  1198. trim(unsigned char *s)
  1199. {
  1200.     unsigned char *p;
  1201.     while (isspace(*s))
  1202.         s++;
  1203.     p=s+strlen(s)-1;
  1204.     while (isspace(*p))
  1205.         p--;
  1206.     p[1]='\0';
  1207.     return s;
  1208. }
  1209.  
  1210. /*****************************************************************************
  1211.  * Gruppennamenskonvertiercode. 
  1212.  * Arbeitet mit zwei alphabetisch sortierten Listen und einem einfachen
  1213.  * "Hashing", das die Zahl der Suchzugriffe schon mal deutlich senkt, ohne
  1214.  * allzu viel Logik zu brauchen.
  1215.  * Beide Listen zeigen auf dieselben Strings!
  1216.  *****************************************************************************
  1217.  */
  1218. unsigned char *
  1219. get_longname(unsigned char *sname)
  1220. {
  1221.     grpconv_t *akt;
  1222.     unsigned char c;
  1223.     c=tolower(*sname);
  1224.     akt=gshash[c];
  1225.     if (akt)
  1226.     {
  1227.         while (akt && tolower(*akt->shortname)==c)
  1228.         {
  1229.             if (!strcasecmp(akt->shortname,sname))
  1230.                 return akt->longname;
  1231.             akt=akt->next;
  1232.         }
  1233.     }
  1234.     /* nicht gefunden - aber getreu dem dont-trust-my-sources prinzip 
  1235.      * wird doch noch mal die ganze Liste durchsucht.
  1236.      */
  1237.     akt=gsanker;
  1238.     while (akt)
  1239.     {
  1240.         if (!strcasecmp(akt->shortname,sname))
  1241.             return akt->longname;
  1242.         akt=akt->next;
  1243.     }
  1244.     return NULL;
  1245. }
  1246.  
  1247. unsigned char *
  1248. get_shortname(unsigned char *lname)
  1249. {
  1250.     grpconv_t *akt;
  1251.     unsigned char c;
  1252.     c=tolower(*lname);
  1253.     akt=glhash[c];
  1254.     if (akt)
  1255.     {
  1256.         while (akt && tolower(*akt->longname)==c)
  1257.         {
  1258.             if (!strcasecmp(akt->longname,lname))
  1259.                 return akt->shortname;
  1260.             akt=akt->next;
  1261.         }
  1262.     }
  1263.     /* nicht gefunden - aber getreu dem dont-trust-my-sources prinzip 
  1264.      * wird doch noch mal die ganze Liste durchsucht.
  1265.      */
  1266.     akt=glanker;
  1267.     while (akt)
  1268.     {
  1269.         if (!strcasecmp(akt->longname,lname))
  1270.             return akt->shortname;
  1271.         akt=akt->next;
  1272.     }
  1273.     /* Nicht gefunden */
  1274.     if (make_shortnames)
  1275.     {
  1276.         char buf[256];
  1277.         char *p;
  1278.         char *p1;
  1279.         size_t l;
  1280.         size_t l1;
  1281.         grpconv_t *lnew,*snew;
  1282.         short war_zu_lang=0;
  1283.  
  1284.         strcpy(buf,lname);
  1285.         if (strlen(buf)>10)
  1286.             war_zu_lang=1;
  1287.  
  1288.         while ((l=strlen(buf))>10)
  1289.         {
  1290.             p=buf;
  1291.             while (*p)
  1292.             {
  1293.                 if (*p==' ')
  1294.                     *p='.';
  1295.                 p++;
  1296.             }
  1297.             p=buf;
  1298.             /* 
  1299.              * Nicht toll, aber ausreichend:
  1300.              *    comp.os.linux.announce
  1301.              * -> com.o.linu.announc
  1302.              * -> co.o.lin.announ
  1303.              * -> c.o.lin.annou
  1304.              * -> c.o.l.anno
  1305.              */
  1306.             l1=l;
  1307.             while (p && *p)
  1308.             {
  1309.                 p1=strchr(p,'.');
  1310.                 if (p1==p)
  1311.                 {
  1312.                     memmove(p,p+1,strlen(p));
  1313.                     l--;
  1314.                 }
  1315.                 else if (!p1[1])
  1316.                 {
  1317.                     *p1='\0';
  1318.                     l--;
  1319.                 }
  1320.                 else if (!p1)
  1321.                 {
  1322.                     /* letztes Zeichen weg */
  1323.                     p[strlen(p)-1]='\0';
  1324.                     l--;
  1325.                     p=NULL;
  1326.                 }
  1327.                 else
  1328.                 {
  1329.                     memmove(p1-1,p1,strlen(p1)+1);
  1330.                     l--;
  1331.                     p=p1;
  1332.                 }
  1333.                 if (l<=10)
  1334.                     break;
  1335.             } 
  1336.             if (l==l1)
  1337.             {
  1338.                 /* Nichts veraendert - gaanz schlecht! */
  1339.                 p=strchr(buf,'.');
  1340.                 if (!p)
  1341.                     break;
  1342.                 memmove(p,p+1,strlen(p1));
  1343.                 l--;
  1344.             }
  1345.         } /* while laenge > 0 */
  1346.         /* Wenn immer noch zu lang oder nicht eindeutig, dann ... */
  1347.         if (war_zu_lang && (strlen(buf)>10 || get_longname(buf)))
  1348.         {
  1349.             fprintf(stderr,"%s: kann keinen Kurznamen fuer %s generieren\n",
  1350.                 myname,lname);
  1351.             return NULL;
  1352.         }
  1353.         snew=xmalloc(sizeof(grpconv_t));
  1354.         lnew=xmalloc(sizeof(grpconv_t));
  1355.         snew->shortname=xmalloc(strlen(buf)+1);
  1356.         snew->longname=xmalloc(strlen(lname)+1);
  1357.         strcpy(snew->longname,lname);
  1358.         strcpy(snew->shortname,buf);
  1359.         *lnew=*snew;
  1360.         grp_einfuegen(snew,lnew);
  1361.         grplist_changed=TRUE;
  1362.         return buf;
  1363.     }
  1364.     return NULL;
  1365. }
  1366.  
  1367. void 
  1368. grp_einfuegen(grpconv_t *snew,grpconv_t *lnew)
  1369. {
  1370.     unsigned char c;
  1371.     grpconv_t *akt,*last;
  1372.     /*
  1373.      * Alphabetisch sortiert in die beiden Listen einfuegen, Eintrag in der
  1374.      * jeweiligen Hashtabelle anlegen.
  1375.      */
  1376.     akt=glanker;
  1377.     c=tolower(lnew->longname[0]);
  1378.     last=NULL;
  1379.     while (akt)
  1380.     {
  1381.         if (strcasecmp(akt->longname,lnew->longname)>0)
  1382.         {
  1383.             lnew->next=akt;
  1384.             if (last)
  1385.             {
  1386.                 last->next=lnew;
  1387.                 /* Hashtabelle updaten */
  1388.                 if (c != tolower(last->longname[0]))
  1389.                     glhash[c]=lnew;
  1390.             }
  1391.             else
  1392.             {
  1393.                 glanker=lnew;
  1394.                 glhash[c]=lnew;
  1395.             }
  1396.             break;
  1397.         }
  1398.         last=akt;
  1399.         akt=akt->next;
  1400.     }
  1401.     if (!akt)
  1402.     {
  1403.         /* nicht gefunden, also kommt also im Alphabet nach allen anderen */
  1404.         if (last)
  1405.         {
  1406.             last->next=lnew;
  1407.             if (c != tolower(last->longname[0]))
  1408.                 glhash[c]=lnew;
  1409.         }
  1410.         else
  1411.         {
  1412.             glanker=lnew;
  1413.             glhash[c]=lnew;
  1414.         }
  1415.         lnew->next=NULL; 
  1416.     }
  1417.     /* und nun dasselbe fuer den kurzen Namen */
  1418.     akt=gsanker;
  1419.     c=tolower(snew->shortname[0]);
  1420.     last=NULL;
  1421.     while (akt)
  1422.     {
  1423.         if (strcasecmp(akt->shortname,snew->shortname)>0)
  1424.         {
  1425.             snew->next=akt;
  1426.             if (last)
  1427.             {
  1428.                 last->next=snew;
  1429.                 /* Hashtabelle updaten */
  1430.                 if (c != tolower(last->shortname[0]))
  1431.                     gshash[c]=snew;
  1432.             }
  1433.             else
  1434.             {
  1435.                 gsanker=snew;
  1436.                 gshash[c]=snew;
  1437.             }
  1438.             break;
  1439.         }
  1440.         last=akt;
  1441.         akt=akt->next;
  1442.     }
  1443.     if (!akt)
  1444.     {
  1445.         /* nicht gefunden, also kommt also im Alphabet nach allen anderen */
  1446.         if (last)
  1447.         {
  1448.             last->next=snew;
  1449.             if (c != tolower(last->shortname[0]))
  1450.                 gshash[c]=snew;
  1451.         }
  1452.         else
  1453.         {
  1454.             gsanker=snew;
  1455.             gshash[c]=snew;
  1456.         }
  1457.         lnew->next=NULL; 
  1458.     }
  1459. }
  1460.  
  1461. void 
  1462. speichere_grplist(const char *fname)
  1463. {
  1464.     FILE *f;
  1465.     grpconv_t *akt;
  1466.  
  1467.     f=fopen(fname,"w");
  1468.     if (!f)
  1469.     {
  1470.         fprintf(stderr,"%s: kann %s nicht schreiben: %s",
  1471.             myname,fname,strerror(errno));
  1472.         exit(1);
  1473.     }
  1474.     akt=glanker;
  1475.     while (akt)
  1476.     {
  1477.         fprintf(f,"%s = %s\n",akt->longname,akt->shortname);
  1478.         akt=akt->next;
  1479.     }
  1480.     fclose(f);
  1481. }
  1482.  
  1483. void 
  1484. lade_grplist(const char *fname)
  1485. {
  1486.     FILE *f;
  1487.     unsigned char buf[1024];
  1488.     unsigned char *p;
  1489.     unsigned char *lname;
  1490.     unsigned char *sname;
  1491.     unsigned int zeile = 0;
  1492.     grpconv_t *news,*newl;
  1493.     f=fopen(fname,"r");
  1494.     if (!f)
  1495.     {
  1496.         fprintf(stderr,"%s: kann %s nicht lesen: %s",
  1497.             myname,fname,strerror(errno));
  1498.         exit(1);
  1499.     }
  1500.     while (fgets(buf,sizeof(buf),f))
  1501.     {
  1502.         zeile++;
  1503.         p=strpbrk(buf,"\r\n");
  1504.         if (p)
  1505.             *p='\0';
  1506.         p=trim(buf);
  1507.         if (*p==';' || *p=='#' || !*p)
  1508.             continue;
  1509.         sname=strchr(buf,'=');
  1510.         if (!sname)
  1511.         {
  1512.             /* Tsss ... */
  1513.             fprintf(stderr,"%s: Zeile %d in %s nicht parsbar",myname,zeile,
  1514.                 fname);
  1515.             fprintf(stderr,"%s\n",buf);
  1516.             continue;
  1517.         }
  1518.         *sname++='\0';
  1519.         lname=trim(buf);
  1520.         sname=trim(sname);
  1521.         if (!*lname || !*sname)
  1522.         {
  1523.             /* Tsss ... */
  1524.             fprintf(stderr,"%s: Gruppenname nicht angegeben in %s, Zeile %d\n",
  1525.                 myname,fname,zeile);
  1526.             continue;
  1527.         }
  1528.         news=xmalloc(sizeof(grpconv_t));
  1529.         newl=xmalloc(sizeof(grpconv_t));
  1530.         sname=strdup(sname);
  1531.         lname=strdup(lname);
  1532.         if (!sname || !lname)
  1533.         {
  1534.             fprintf(stderr,"%s: Kein Speicher mehr verfuegbar\n", myname);
  1535.             exit(1);
  1536.         }
  1537.         news->longname=lname;
  1538.         news->shortname=sname;
  1539.         newl->longname=lname;
  1540.         newl->shortname=sname;
  1541.         grp_einfuegen(news,newl);
  1542.     }
  1543.     fclose(f);
  1544. }
  1545.  
  1546. /*****************************************************************************
  1547.  * Hauptprogramm: Kommandozeilenauswertung und Dateihandling.
  1548.  *****************************************************************************
  1549.  */
  1550. int
  1551. main (int argc, char **argv)
  1552. {
  1553.     int i;
  1554.     char *in_name = NULL;
  1555.     char *out_name = NULL;
  1556.     char *grp_name = NULL;
  1557.     FILE *in = stdin;                   /* Default */
  1558.     FILE *out = stdout;                   /* Default */
  1559.     int default_buffer = 1;               /* Defaultpuffer verwenden */
  1560.     long buffersize = 0;               /* Groess des Puffers in K */
  1561.     size_t calc_buffersize = 0;           /* berechnete wirkliche Puffergroesse */
  1562.     char tmpfname[PATH_MAX];
  1563.     char *probsfname = NULL;
  1564.     int in_place = 0;
  1565.     char *p;
  1566.  
  1567.  
  1568.     if (argv[0] && *argv[0])
  1569.         myname = argv[0];
  1570.  
  1571.     (void) (argc);                /* Keep PureC and BorlandC quiet */
  1572.  
  1573.     i = 1;
  1574.     /* getopt kennt zumindest die PureC-Lib nicht */
  1575.     while (argv[i] != NULL)
  1576.     {
  1577.         if (argv[i][0] == '-')
  1578.         {
  1579.             switch (argv[i][1])
  1580.             {
  1581.             case '0':
  1582.                 discard_false_ids = TRUE;
  1583.                 break;
  1584.             case '1':
  1585.                 if (argv[i][2] == '6')
  1586.                     quark16 = TRUE;
  1587.                 else
  1588.                 {
  1589.                     fprintf (stderr, "%s: Fuer welche Quark?\n", myname);
  1590.                     exit (2);
  1591.                 }
  1592.                 break;
  1593.             case 'b':
  1594.                 if (argv[i + 1])
  1595.                 {
  1596.                     default_buffer = 0;
  1597.                     buffersize = atol (argv[++i]);
  1598.                     if (buffersize < 0)
  1599.                     {
  1600.                         fprintf (stderr,
  1601.                                "%s: Puffergroesse muss > 0 sein\n", myname);
  1602.                         exit (2);
  1603.                     }
  1604.                 }
  1605.                 else
  1606.                 {
  1607.                     fprintf (stderr, "%s: Puffergroesse?\n", myname);
  1608.                     exit (2);
  1609.                 }
  1610.                 break;
  1611.             case 'B':
  1612.                 if (argv[i + 1])
  1613.                     fake_box = argv[++i];
  1614.                 else
  1615.                 {
  1616.                     fprintf (stderr, "%s: Welcher Boxtyp?\n", myname);
  1617.                     exit (2);
  1618.                 }
  1619.                 break;
  1620.             case 'd':
  1621.                 if (argv[i + 1])
  1622.                 {
  1623.                     showdots = atol (argv[++i]);
  1624.                     if (showdots < 0)
  1625.                     {
  1626.                         fprintf (stderr,
  1627.                                  "%s: Msgs/Punkt muss > 0 sein\n",
  1628.                                  myname);
  1629.                         exit (2);
  1630.                     }
  1631.                 }
  1632.                 else
  1633.                 {
  1634.                     fprintf (stderr, "%s: buffersize?\n", myname);
  1635.                     exit (2);
  1636.                 }
  1637.                 break;
  1638.             case 'F':
  1639.                 if (argv[i + 1])
  1640.                     fake_version = argv[++i];
  1641.                 else
  1642.                 {
  1643.                     fprintf (stderr, "%s: Versionsnummer?\n", myname);
  1644.                     exit (2);
  1645.                 }
  1646.                 break;
  1647.             case 'g':
  1648.             case 'G':
  1649.                 if (argv[i + 1])
  1650.                 {
  1651.                     /* das jeweils andere Flag loeschen, um bei 
  1652.                      * -g -G den Supergau zu vermeiden 
  1653.                      */
  1654.                     if (argv[i][1]=='G')
  1655.                     {
  1656.                         gconv2long=TRUE;
  1657.                         gconv2short=FALSE;
  1658.                     }
  1659.                     else
  1660.                     {
  1661.                         gconv2long=FALSE;
  1662.                         gconv2short=TRUE;
  1663.                     }
  1664.                     grp_name = argv[++i];
  1665.                 }
  1666.                 else
  1667.                 {
  1668.                     fprintf (stderr, "%s: Gruppendateiname?\n", myname);
  1669.                     exit (2);
  1670.                 }
  1671.                 break;
  1672.             case '?':
  1673.             case 'h':
  1674.                 usage ();
  1675.                 exit (0);
  1676.                 break;
  1677.             case 'H':
  1678.                 headfilt=TRUE;
  1679.                 break;
  1680.             case 'i':
  1681.                 if (argv[i + 1])
  1682.                     in_name = argv[++i];
  1683.                 else
  1684.                 {
  1685.                     fprintf (stderr, "%s: infilename?\n", myname);
  1686.                     exit (2);
  1687.                 }
  1688.                 break;
  1689.             case 'K':
  1690.                 savekopfaskopf = TRUE;
  1691.                 break;
  1692.             case 'l':
  1693.                 long2short_ids=TRUE;
  1694.                 short2long_ids=FALSE;
  1695.                 break;
  1696.             case 'L':
  1697.                 long2short_ids=FALSE;
  1698.                 short2long_ids=TRUE;
  1699.                 break;
  1700.             case 'o':
  1701.                 if (argv[i + 1])
  1702.                     out_name = argv[++i];
  1703.                 else
  1704.                 {
  1705.                     fprintf (stderr, "%s: outfilename?\n", myname);
  1706.                     exit (2);
  1707.                 }
  1708.                 break;
  1709.             case 'p':
  1710.                 if (argv[i + 1])
  1711.                     probsfname = argv[++i];
  1712.                 else
  1713.                 {
  1714.                     fprintf (stderr, "%s: Problemfilename?\n", myname);
  1715.                     exit (2);
  1716.                 }
  1717.                 break;
  1718.             case 's':
  1719.                 make_shortnames = TRUE;
  1720.                 break;
  1721.             case 'S':
  1722.                 if (argv[i + 1])
  1723.                     limit_subject = (unsigned int) atoi (argv[++i]);
  1724.                 else
  1725.                 {
  1726.                     fprintf (stderr, "%s: Betrefflaenge?\n", myname);
  1727.                     exit (2);
  1728.                 }
  1729.                 break;
  1730.             case 'T':
  1731.                 savekopfastext = TRUE;
  1732.                 break;
  1733.             case 'U':
  1734.                 kill_umlaute = TRUE;
  1735.                 break;
  1736.             case 'v':
  1737.                 fprintf (stderr, "%s version %s\n", myname, VERSIONSTRING);
  1738.                 break;            /* kein exit! */
  1739.             default:
  1740.                 fprintf (stderr, "%s: Option %s ist mir unbekannt\n",
  1741.                          myname, argv[i]);
  1742.                 usage ();
  1743.                 exit (1);
  1744.                 break;
  1745.             }
  1746.         }
  1747.         else
  1748.         {
  1749.             fprintf (stderr, "%s: unbekannter Parameter %s\n",
  1750.                      myname, argv[i]);
  1751.             usage ();
  1752.             exit (2);
  1753.         }
  1754.         i++;
  1755.     }                            /* while */
  1756.  
  1757.  
  1758.     p = getenv ("QUARKVERSION");
  1759.     if (p)
  1760.     {
  1761.         if (!strcmp (p, "16") || !strcmp (p, "1.6"))
  1762.             quark16 = 1;
  1763.     }
  1764.     p=getenv("COLUMNS");
  1765.     if (p)
  1766.     {
  1767.         columns=strtol(p,NULL,10);
  1768.         if (columns<=0)
  1769.             columns=80;
  1770.     }
  1771.  
  1772.     if (grp_name)
  1773.         lade_grplist(grp_name);
  1774.  
  1775.     if (in_name && out_name && !strcmp(in_name,out_name) &&strcmp(in_name,"-"))
  1776.     {
  1777.         in_place=1;
  1778.         /* temporaerdatei im Verzeichnis des Infiles anlegen, das 
  1779.          * spart die Zeit zum Kopieren
  1780.          */
  1781.         /* Ach, ist das schoen, wie vielseitig doch die Dateisysteme 
  1782.          * auf der Welt sind. Nur schade, dass noch nirgendwo das 
  1783.          * kleine e als Trennzeichen verwendet wird :-)
  1784.          */
  1785. #if defined(__TOS__) || defined(__DOS__) || defined(__OS2__)
  1786.         p=strrchr(in_name,'\\');
  1787. #else
  1788.         p=strrchr(in_name,'/');
  1789. #endif
  1790.         if (p)
  1791.         {
  1792.             strcpy(tmpfname,p+1);
  1793.             strcat(tmpfname,"qoutfile.tmp");
  1794.         }
  1795.         else
  1796.             strcpy(tmpfname,"qoutfile.tmp");
  1797.     }
  1798.  
  1799.     if (!default_buffer)
  1800.     {
  1801.         calc_buffersize = (size_t) buffersize *1024;
  1802.  
  1803.         /* Fuer Systeme mit size_t == 2 Byte - absolut braindamaged */
  1804.         if (sizeof (size_t) == sizeof (short))
  1805.         {
  1806.             if (buffersize * 1024 > USHRT_MAX)
  1807.             {
  1808.                 fprintf (stderr,
  1809.                          "%s: Puffer zu gross. Verwende Maximum (%ld).\n",
  1810.                          myname, (long) (USHRT_MAX / 1024));
  1811.                 calc_buffersize = USHRT_MAX / 1024;
  1812.             }
  1813.         }
  1814.     }
  1815.  
  1816.     if (in_name && strcmp(in_name,"-"))
  1817.     {
  1818. #ifdef HAVE_MMAP
  1819.         int fd;
  1820.         struct stat st;
  1821.         fd=open(in_name,O_RDONLY);
  1822.         if (fd<0)
  1823.         {
  1824.             fprintf (stderr, "%s: cannot open %s for reading (%s)\n",
  1825.                      myname, in_name, strerror (errno));
  1826.             exit (1);
  1827.         }
  1828.         if (fstat(fd,&st))
  1829.         {
  1830.             fprintf (stderr, "%s: cannot stat %s (%s)\n",
  1831.                      myname, in_name, strerror (errno));
  1832.             exit (1);
  1833.         }
  1834.         mmaddr=mmap(0,st.st_size,
  1835.             PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
  1836.         if (mmaddr==(caddr_t) -1)
  1837.         {
  1838.             fprintf (stderr, "%s: cannot mmap %s (%s)\n",
  1839.                      myname, in_name, strerror (errno));
  1840.             in=fdopen(fd,"r");
  1841.             if (!in)
  1842.             {
  1843.                 fprintf (stderr, "%s: cannot open %s for reading (%s)\n",
  1844.                      myname, in_name, strerror (errno));
  1845.                 exit (1);
  1846.             }
  1847.             mmaddr=(caddr_t)-1;
  1848.         }
  1849.         else
  1850.         {
  1851.             mmsize=st.st_size;
  1852.             /* dirty trick: String abschliessen mit \0. Ist eigentlich Schmutz,
  1853.              * aber als letztes Zeichen *sollte* immer ein \n kommen, und
  1854.              * darauf kann quarkoutfile auch verzichten.
  1855.              */
  1856.             ((char *) mmaddr)[mmsize-1]='\0';
  1857.             close(fd);
  1858.             is_mmapped=1;
  1859.         }
  1860. #else
  1861.         in = fopen (in_name, "r");
  1862.         if (!in)
  1863.         {
  1864.             fprintf (stderr, "%s: cannot open %s for reading (%s)\n",
  1865.                      myname, in_name, strerror (errno));
  1866.             exit (1);
  1867.         }
  1868. #endif
  1869.     }
  1870. #ifdef HAVE_MMAP
  1871.     else
  1872.     {
  1873.         struct stat st;
  1874.         mmaddr=(caddr_t) -1;
  1875.         /* Versuchen wir mal, stdin zu mmappen */
  1876.         if (fstat(0,&st)==0 && st.st_size)
  1877.             mmaddr=mmap(0,st.st_size,PROT_READ|PROT_WRITE,MAP_PRIVATE,0,0);
  1878.         if (mmaddr!=(caddr_t) -1)
  1879.         {
  1880.             mmsize=st.st_size;
  1881.             /* dirty trick: String abschliessen mit \0. Ist eigentlich Schmutz,
  1882.              * aber als letztes Zeichen *sollte* immer ein \n kommen, und
  1883.              * darauf kann quarkoutfile auch verzichten.
  1884.              */
  1885.             ((char *) mmaddr)[mmsize-1]='\0';
  1886.             close(0);
  1887.             is_mmapped=1;
  1888.         }
  1889.         else
  1890.             mmaddr=(caddr_t)-1;
  1891.     }
  1892. #endif
  1893.  
  1894.     if (out_name && !in_place && strcmp(out_name,"-"))
  1895.     {
  1896.         out = fopen (out_name, "w");
  1897.         if (!out)
  1898.         {
  1899.             fprintf (stderr, "%s: cannot open %s for writing (%s)\n",
  1900.                      myname, out_name, strerror (errno));
  1901.             exit (1);
  1902.         }
  1903.     }
  1904.     if (probsfname)
  1905.     {
  1906.         probsfile = fopen (probsfname, "w");
  1907.         if (!probsfile)
  1908.         {
  1909.             fprintf (stderr, "%s: cannot open %s for writing (%s)\n",
  1910.                      myname, probsfname, strerror (errno));
  1911.             exit (1);
  1912.         }
  1913.         /* Die Datei zeilenweise puffern. */
  1914.         setvbuf (probsfile, NULL, _IOLBF, 0);
  1915.     }
  1916.  
  1917.     if (in_place)
  1918.     {
  1919.         out = fopen (tmpfname, "w");
  1920.         if (!out)
  1921.         {
  1922.             fprintf (stderr, "%s: kann temporaere Datei %s nicht oeffnen (%s)\n",
  1923.                      myname, tmpfname, strerror (errno));
  1924.             exit (1);
  1925.         }
  1926.     }
  1927.  
  1928.     if (!default_buffer)
  1929.     {
  1930.         if (buffersize == 0)
  1931.         {
  1932. #ifdef HAVE_MMAP
  1933.             if (!is_mmapped)
  1934. #endif
  1935.                 setvbuf (in, NULL, _IONBF, 0);    /* ungepuffert */
  1936.             setvbuf (out, NULL, _IONBF, 0);        /* ungepuffert */
  1937.         }
  1938.         else
  1939.         {
  1940.             /*
  1941.              * Linux libc (V 4.5.19) laesst bei setvbuf(f,NULL,...)
  1942.              * fast alles unveraendert. Was soll das?
  1943.              * Immerhin ist das schon besser als das Verhalten von
  1944.              * libc (Version 4.4.4), die hat da naemlich auf ungepuffert
  1945.              * geschaltet ...
  1946.              */
  1947.             char *bin=NULL;
  1948.             char *bout = malloc (calc_buffersize);
  1949. #ifdef HAVE_MMAP
  1950.             if (!is_mmapped)
  1951.                 bin = malloc (calc_buffersize);
  1952. #endif
  1953.  
  1954.             if (bin && setvbuf (in, bin, _IOFBF, calc_buffersize))
  1955.             {
  1956.                 /* Fehlschlag, aber das stoert nicht die Funktion des Tools */
  1957.                 fprintf (stderr, "%s: couldn't allocate outputbuffer\n",
  1958.                          myname);
  1959.             }
  1960.             if (bout && setvbuf (out, bout, _IOFBF, calc_buffersize))
  1961.             {
  1962.                 /* Fehlschlag, aber das stoert nicht die Funktion des Tools */
  1963.                 fprintf (stderr, "%s: couldn't allocate outputbuffer\n",
  1964.                          myname);
  1965.             }
  1966.         }
  1967.     }
  1968.     if (showdots && out == stdout)
  1969.     {
  1970.         dotsfile = stderr;
  1971.     }
  1972.  
  1973.     quarkoutfile (in, out);
  1974. #ifdef HAVE_MMAP
  1975.     if (is_mmapped)
  1976.         munmap(mmaddr,mmsize);
  1977. #endif
  1978.  
  1979.     if (grp_name && grplist_changed)
  1980.         speichere_grplist(grp_name);
  1981.  
  1982.     if (in_place)
  1983.     {    
  1984.         /* GEMDOS, langsam und dumm, kann geoeffnete Dateien nicht
  1985.          * fehlerfrei umbenennen 
  1986.          */
  1987.         fclose(out);
  1988.         if (rename(tmpfname,in_name))
  1989.         {
  1990.             /* ? Warum hat das nicht geklappt? */
  1991.             fprintf (stderr, "%s: kann temporaerdatei nicht umbenennen (%s)\n",
  1992.                  myname,strerror(errno));
  1993.             exit(1);
  1994.         }
  1995.     }
  1996.     return (0);                    /* return, not exit, for pure's sake */
  1997. }
  1998.